# LE Grammar for LE Grammars
# 
# Copyright (c) 2007 by Ian Piumarta
# All rights reserved.
# 
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the 'Software'),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, provided that the above copyright notice(s) and this
# permission notice appear in all copies of the Software.  Acknowledgement
# of the use of this Software in supporting documentation would be
# appreciated but is not required.
# 
# THE SOFTWARE IS PROVIDED 'AS IS'.  USE ENTIRELY AT YOUR OWN RISK.
# 
# Last edited: 2016-07-22 09:45:53 by piumarta on zora.local

%{
# include "tree.h"
# include "version.h"

# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <libgen.h>
# include <assert.h>

  typedef struct Header Header;

  struct Header {
    int	    line;
    char   *text;
    Header *next;
  };

  FILE *input= 0;

  int   verboseFlag= 0;
  int   nolinesFlag= 0;

  static int	 lineNumber= 0;
  static int	 headerLine= 0;
  static int	 actionLine= 0;
  static char	*fileName= 0;
  static int	 trailerLine= 0;
  static char	*trailer= 0;
  static Header	*headers= 0;

  void makeHeader(int line, char *text);
  void makeTrailer(int line, char *text);

  void yyerror(char *message);

# define YY_INPUT(buf, result, max)			\
  {							\
    int c= getc(input);					\
    /* if ('\n' == c || '\r' == c) ++lineNumber; */	\
    result= (EOF == c) ? 0 : (*(buf)= c, 1);		\
  }

# define YY_LOCAL(T)	static T
# define YY_RULE(T)	static T
%}

# Hierarchical syntax

grammar=	- ( declaration | definition )+ trailer? end-of-file

declaration=	'%{'					{ headerLine= lineNumber; }
		< ( !'%}' (end-of-line | .) )* >
		RPERCENT				{ makeHeader(headerLine, yytext); }				#{YYACCEPT}

trailer=	'%%'					{ headerLine= lineNumber }
		< .* >					{ makeTrailer(headerLine, yytext); }				#{YYACCEPT}

definition=	identifier 				{ if (push(beginRule(findRule(yytext)))->rule.expression)
							    fprintf(stderr, "rule '%s' redefined\n", yytext); }
			EQUAL expression		{ Node *e= pop();  Rule_setExpression(pop(), e); }
			SEMICOLON?											#{YYACCEPT}

expression=	sequence (BAR sequence			{ Node *f= pop();  push(Alternate_append(pop(), f)); }
			    )*

sequence=	error   (error				{ Node *f= pop();  push(Sequence_append(pop(), f)); }
			)*

error=		prefix  (TILDE action			{ push(makeError(pop(), yytext)); }
			)?

prefix=		AT  action				{ push(makeInline(yytext)); }
|		AND action				{ push(makePredicate(yytext)); }
|		AND suffix				{ push(makePeekFor(pop())); }
|		NOT suffix				{ push(makePeekNot(pop())); }
|		    suffix

suffix=		primary (QUESTION			{ push(makeQuery(pop())); }
			     | STAR			{ push(makeStar (pop())); }
			     | PLUS			{ push(makePlus (pop())); }
			   )?

primary=	identifier				{ push(makeVariable(yytext)); }
			COLON identifier !EQUAL		{ Node *name= makeName(findRule(yytext));  name->name.variable= pop();  push(name); }
|		identifier !EQUAL			{ push(makeName(findRule(yytext))); }
|		OPEN expression CLOSE
|		literal					{ push(makeString(yytext)); }
|		class					{ push(makeClass(yytext)); }
|		DOT					{ push(makeDot()); }
|		action					{ push(makeAction(actionLine, yytext)); }
|		BEGIN					{ push(makePredicate("YY_BEGIN")); }
|		END					{ push(makePredicate("YY_END")); }

# Lexical syntax

identifier=	< [-a-zA-Z_][-a-zA-Z_0-9]* > -

literal=	['] < ( !['] char )* > ['] -
|		["] < ( !["] char )* > ["] -

class=		'[' < ( !']' range )* > ']' -

range=		char '-' char | char

char=		'\\' [-abefnrtv'"\[\]\\]
|		'\\' 'x'[0-9A-Fa-f][0-9A-Fa-f]
|		'\\' 'x'[0-9A-Fa-f]
|		'\\' [0-3][0-7][0-7]
|		'\\' [0-7][0-7]?
|		!'\\' .

action=		'{'					{ actionLine= lineNumber }
		< braces* > '}' -

braces=		'{' braces* '}'
|		!'}' ( end-of-line | . )

EQUAL=		'=' -
COLON=		':' -
SEMICOLON=	';' -
BAR=		'|' -
AND=		'&' -
NOT=		'!' -
AT=		'@' -
QUESTION=	'?' -
STAR=		'*' -
PLUS=		'+' -
OPEN=		'(' -
CLOSE=		')' -
DOT=		'.' -
BEGIN=		'<' -
END=		'>' -
TILDE=		'~' -
RPERCENT=	'%}' -

-=		(space | comment)*
space=		' ' | '\t' | end-of-line
comment=	'#' (!end-of-line .)* end-of-line
end-of-line=	( '\r\n' | '\n' | '\r' )		{ ++lineNumber }
end-of-file=	!.

%%

void yyerror(char *message)
{
  fprintf(stderr, "%s:%d: %s", fileName, lineNumber, message);
  if (yyctx->__text[0]) fprintf(stderr, " near token '%s'", yyctx->__text);
  if (yyctx->__pos < yyctx->__limit || !feof(input))
    {
      yyctx->__buf[yyctx->__limit]= '\0';
      fprintf(stderr, " before text \"");
      while (yyctx->__pos < yyctx->__limit)
	{
	  if ('\n' == yyctx->__buf[yyctx->__pos] || '\r' == yyctx->__buf[yyctx->__pos]) break;
	  fputc(yyctx->__buf[yyctx->__pos++], stderr);
	}
      if (yyctx->__pos == yyctx->__limit)
	{
	  int c;
	  while (EOF != (c= fgetc(input)) && '\n' != c && '\r' != c)
	    fputc(c, stderr);
	}
      fputc('\"', stderr);
    }
  fprintf(stderr, "\n");
  exit(1);
}

void makeHeader(int line, char *text)
{
  Header *header= (Header *)malloc(sizeof(Header));
  header->line= line;
  header->text= strdup(text);
  header->next= headers;
  headers= header;
}

void makeTrailer(int line, char *text)
{
  trailerLine= line;
  trailer= strdup(text);
}

static void version(char *name)
{
  printf("%s version %d.%d.%d\n", name, PEG_MAJOR, PEG_MINOR, PEG_LEVEL);
}

static void usage(char *name)
{
  version(name);
  fprintf(stderr, "usage: %s [<option>...] [<file>...]\n", name);
  fprintf(stderr, "where <option> can be\n");
  fprintf(stderr, "  -h          print this help information\n");
  fprintf(stderr, "  -o <ofile>  write output to <ofile>\n");
  fprintf(stderr, "  -P          do not generate #line directives\n");
  fprintf(stderr, "  -v          be verbose\n");
  fprintf(stderr, "  -V          print version number and exit\n");
  fprintf(stderr, "if no <file> is given, input is read from stdin\n");
  fprintf(stderr, "if no <ofile> is given, output is written to stdout\n");
  exit(1);
}

int main(int argc, char **argv)
{
  Node *n;
  int   c;

  output= stdout;
  input= stdin;
  lineNumber= 1;
  fileName= "<stdin>";

  while (-1 != (c= getopt(argc, argv, "PVho:v")))
    {
      switch (c)
	{
	case 'V':
	  version(basename(argv[0]));
	  exit(0);

	case 'h':
	  usage(basename(argv[0]));
	  break;

	case 'o':
	  if (!(output= fopen(optarg, "w")))
	    {
	      perror(optarg);
	      exit(1);
	    }
	  break;

	case 'P':
	  nolinesFlag= 1;
	  break;

	case 'v':
	  verboseFlag= 1;
	  break;

	default:
	  fprintf(stderr, "for usage try: %s -h\n", argv[0]);
	  exit(1);
	}
    }
  argc -= optind;
  argv += optind;

  if (argc)
    {
      for (;  argc;  --argc, ++argv)
	{
	  if (!strcmp(*argv, "-"))
	    {
	      input= stdin;
	      fileName= "<stdin>";
	    }
	  else
	    {
	      if (!(input= fopen(*argv, "r")))
		{
		  perror(*argv);
		  exit(1);
		}
	      fileName= *argv;
	    }
	  lineNumber= 1;
	  if (!yyparse())
	    yyerror("syntax error");
	  if (input != stdin)
	    fclose(input);
	}
    }
  else
    if (!yyparse())
      yyerror("syntax error");

  if (verboseFlag)
    for (n= rules;  n;  n= n->any.next)
      Rule_print(n);

  Rule_compile_c_header();

  for (; headers;  headers= headers->next)
    fprintf(output, "#line %i \"%s\"\n%s\n", headers->line, fileName, headers->text);

  if (rules)
    Rule_compile_c(rules, nolinesFlag);

  if (trailer) {
    if (!nolinesFlag)
      fprintf(output, "#line %i \"%s\"\n", trailerLine, fileName); /* , trailer); */
    fprintf(output, "%s\n", trailer);
  }
  if (0) {
    yySet (NULL, NULL, 0);
    yyPop (NULL, NULL, 0);
    yyPush (NULL, NULL, 0);
    yyAccept (NULL, 0);
  }
  return 0;
}
